﻿#region

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using JsMapper.BO;
using JsMapper.BO.Attributes;

#endregion

namespace JsMapper.BL
{
    public static class JsModelBuilder
    {
        public static JsModel<T> Create<T>() where T : class
        {
            var attr = GetAttributeOrDefault<T>();
            var props = GetAllProps<T>();

            return new JsModel<T>
                {
                    Name = attr.Name,
                    Url = attr.Url,
                    JsNamespace = attr.JsNamespace,
                    Properties = props,
                    IdProperty = GetIdProp(props),
                    Validations = GetAllPropsWithValidation(props),
                    DefaultValues = GetAllPropsWithDefaultValue(props),
                };
        }

        public static IJsModel Create(Type t)
        {
            var meth = typeof(JsModelBuilder).GetMethod("Create", Type.EmptyTypes);
            var genMeth = meth.MakeGenericMethod(new[] { t });

            var model = genMeth.Invoke(null, null);
            return (IJsModel)model;
        }

        private static PropertyInfo GetIdProp(IEnumerable<PropertyInfo> props)
        {
            return props.FirstOrDefault(x => Attribute.IsDefined(x, typeof (KeyAttribute)));
        }

        private static JsModelAttribute GetAttributeOrDefault<T>()
        {
            var attr = typeof (T).GetCustomAttribute<JsModelAttribute>();
            if (attr == null) return new JsModelAttribute {Name = typeof (T).Name};

            if (string.IsNullOrEmpty(attr.Name)) attr.Name = typeof (T).Name;

            return attr;
        }

        private static List<PropertyInfo> GetAllProps<T>() where T : class
        {
            return
                typeof (T).GetProperties()
                          .Where(x => !Attribute.IsDefined(x, typeof (IgnoreForJsModelAttribute)))
                          .ToList();
        }

        private static Dictionary<PropertyInfo, ValidationAttribute[]> GetAllPropsWithValidation(
            IEnumerable<PropertyInfo> props)
        {
            var ret = new Dictionary<PropertyInfo, ValidationAttribute[]>();

            foreach (var p in props)
            {
                var valAttrs = p.GetCustomAttributes<ValidationAttribute>().ToArray();
                if (valAttrs.Length == 0) continue;

                ret.Add(p, valAttrs);
            }

            return ret;
        }

        private static Dictionary<PropertyInfo, DefaultValueAttribute> GetAllPropsWithDefaultValue(
            IEnumerable<PropertyInfo> props)
        {
            var ret = new Dictionary<PropertyInfo, DefaultValueAttribute>();

            foreach (var p in props)
            {
                var dvAttr = p.GetCustomAttribute<DefaultValueAttribute>();
                if (dvAttr == null) continue;

                ret.Add(p, dvAttr);
            }

            return ret;
        }
    }
}